/*
 * @file      AnalyzeStructureTest.cpp
 * @brief     AnalyzeStructureTest is a test file to AnalizeStructure class.
 *
 * @author    Franco Riberi
 * @email     fgriberi AT gmail.com
 *
 * Contents:  Source file.
 *
 * System:    titere: Target Ideal TEmperature Rna Expression
 * Language:  C++
 *
 * @date February 3, 2015
 *
 * Copyright (C) 2015 Franco Riberi, FuDePAN
 *
 * This file is part of titere.
 *
 * titere is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * titere is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with titere. If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <biopp/biopp.h>
#include <gtest/gtest.h>

#define private public
#include "titere/AnalyzeStructures.h"
#include "titere/Exceptions.h"

using namespace NSTitere;

class AnalyzeStructureSuiteTest : public ::testing::Test
{
protected:
    virtual void SetUp()
    {
        _args.folder = "UNAFold";
        _args.initialRangeTemp = 38;
        _args.finalRangeTemp = 41;
        _args.fileNameRNAm = "/tmp/obsoleteSeq.fasta";
        analyzeStructure = new AnalyzeStructure(_args);
    }

    virtual void TearDown()
    {
        delete analyzeStructure;
    }

private:
    TitereInputArgs _args;
    AnalyzeStructure* analyzeStructure;
};

TEST_F(AnalyzeStructureSuiteTest, AnalyzeStructure)
{
    EXPECT_TRUE(analyzeStructure->_folder != NULL);
    EXPECT_TRUE(analyzeStructure->_rules.size() == 3u);

}

TEST_F(AnalyzeStructureSuiteTest, checkRulesReturnTrue)
{
    const biopp::NucSequence sequence("UGCAUUCUGGACAGGCGGCCU");
    const bool checkResult = analyzeStructure->checkRules(sequence);
    EXPECT_TRUE(checkResult);
}

TEST_F(AnalyzeStructureSuiteTest, checkRulesReturnFalse)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    const bool checkResult = analyzeStructure->checkRules(sequence);
    EXPECT_FALSE(checkResult);
}

TEST_F(AnalyzeStructureSuiteTest, getSubSequence)
{
    const biopp::NucSequence origSequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::NucSequence subSequence;
    analyzeStructure->getSubSequence(2u, 10u, origSequence, subSequence);

    const std::string retrivedSubSequence = subSequence.getString();
    EXPECT_TRUE(retrivedSubSequence == "AAUGCAUUC");

    subSequence.clear();
    EXPECT_THROW(analyzeStructure->getSubSequence(2u, origSequence.length(), origSequence, subSequence), InvalidPosition);
}

TEST_F(AnalyzeStructureSuiteTest, foldSequences)
{
    const std::string inputFile = "/tmp/obsoleteSeq.fasta";
    const std::string descriptionSequence1 = ">YAR053W";
    const std::string descriptionSequence2 = ">YAR029W";
    std::ofstream file(inputFile.c_str());
    file << descriptionSequence1 << " \n";
    file << "ATGTATGAGTACTTATTATTAACGAGGAAAAATGCCCTATTTTCTTTAGCAATTAATGAACCATCG"
         "CCAACTTTTGCTTTAACAATTATTGCCATTTTCAGCAGTACTAACGTAA\n";
    file << descriptionSequence2 << " \n";
    file << "ATGAATAAATATCTATTTGACCATAAAATATGGAGTACTCCTTACTACTTTTATTGCGAAGAAGAT"
         "TGCCACCGTCTTTTTCTAAGTTTTATTGAGGGAAGAACTTTCGAGAAGCCAACAAGCAACGCTGAG"
         "GAAAATGTACAGGAGACTGAAGCTGGCGAATCTTTCACATTAAATCCCGGAGAAGATTTTCAAAAT"
         "TGCTTTCCAAGACAGCGGATATTGTAA \n";
    file.close();

    EXPECT_NO_THROW(analyzeStructure->foldSequences());
    EXPECT_TRUE(analyzeStructure->_sequencesFolded.size() == 2u);
}

TEST_F(AnalyzeStructureSuiteTest, foldInAllTemps)
{
    const std::string stringSequence = "GCCACCTGCATTCTGGACAGGCGGCCTGCCTCGTGCGGCACTTGTGTGCGGGACTGCTGGCCCGAAAC"
                                       "CGGGTCGGTGAGATTCCCTTTCCACAGGTGTGGCGCGGGACCGAGGCTGACCAGAGACTTGGAAGCTGTG"
                                       "CCCTTCGTCAATAGGACAACTCCCTTCACCATAAGGGGCCCCCTGGGAAACCAGGGGAGAGGCAACCCGG"
                                       "TGCGGTCGCCCCTGGGTTTTGGGTCCTACACCATGACCAAGATCCGGGACTCCTTACATCTGGTGAAATG"
                                       "TCCCACCCCGGCCATTGAGCCTCCTACCGGAACGTTTGGGTTCTTCCCGGGAGTCCCCCCCCTTAACAAC"
                                       "TGCATGCTTCTAGGCACGGAAGTGTCTGAGGTATTGGGCGGGGCGGGCCTCACCGGGGGGTTCTATGAGC"
                                       "CTCTGGTACGGCGGTGTTCAGAGCTGATGGGTCGGCGAAATCCGGTCTGCCCGGGGTTTGCGTGGCTCTC"
                                       "CTCTGGGCGGCCTGATGGGTTTATACATGTTCA";
    const biopp::NucSequence sequence(stringSequence);
    AnalyzeStructure::SequencesFoldedContainer containerOfFolding;
    EXPECT_NO_THROW(analyzeStructure->foldInAllTemps(sequence, containerOfFolding));
    EXPECT_TRUE(containerOfFolding._structures.size() == 4u); //38, 39, 40 and 41
}

TEST_F(AnalyzeStructureSuiteTest, countNucleotidesUnpaired)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure structure;
    analyzeStructure->_folder->fold(sequence, _args.circular, structure, _args.initialRangeTemp);
    //structure --> 13 12 11 25 25 25 25 25 25 25 25 2 1 0 24 25 25 23 22 25 25 25 18 17 14

    const size_t lastPos = 24u;
    size_t amountOfUnpaired = analyzeStructure->countNucleotidesUnpaired(0u, lastPos, structure);
    EXPECT_TRUE(amountOfUnpaired == 6u);

    amountOfUnpaired = analyzeStructure->countNucleotidesUnpaired(0u, 4u, structure);
    EXPECT_TRUE(amountOfUnpaired == 1u);

    amountOfUnpaired = analyzeStructure->countNucleotidesUnpaired(3u, 8u, structure);
    EXPECT_TRUE(amountOfUnpaired == 3u);

    EXPECT_THROW(analyzeStructure->countNucleotidesUnpaired(0u, lastPos + 1, structure), InvalidPosition);
}

TEST_F(AnalyzeStructureSuiteTest, calculateUnpaired)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure structure;
    analyzeStructure->_folder->fold(sequence, _args.circular, structure, _args.initialRangeTemp);

    size_t unpaired;
    size_t headUnpaired;
    analyzeStructure->calculateUnpaired(structure, unpaired, headUnpaired);
    EXPECT_TRUE(unpaired == 6u);
    EXPECT_TRUE(headUnpaired == 2u);
}

TEST_F(AnalyzeStructureSuiteTest, processUnpairedOnReferenceStruct)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure structure;
    analyzeStructure->_folder->fold(sequence, _args.circular, structure, _args.initialRangeTemp);

    std::string diffStruct = "00+";
    analyzeStructure->processUnpairedOnReferenceStruct(0u, structure, diffStruct); //paired
    EXPECT_EQ(diffStruct, "00+-");

    analyzeStructure->processUnpairedOnReferenceStruct(11u, structure, diffStruct); //paired
    EXPECT_EQ(diffStruct, "00+--");

    analyzeStructure->processUnpairedOnReferenceStruct(3u, structure, diffStruct); //unpaired
    EXPECT_EQ(diffStruct, "00+--0");

    analyzeStructure->processUnpairedOnReferenceStruct(5u, structure, diffStruct); //unpaired
    EXPECT_EQ(diffStruct, "00+--00");

    analyzeStructure->processUnpairedOnReferenceStruct(14u, structure, diffStruct); //paired
    EXPECT_EQ(diffStruct, "00+--00-");

    analyzeStructure->processUnpairedOnReferenceStruct(18u, structure, diffStruct); //paired
    EXPECT_EQ(diffStruct, "00+--00--");

    analyzeStructure->processUnpairedOnReferenceStruct(23u, structure, diffStruct); //paired
    EXPECT_EQ(diffStruct, "00+--00---");

    analyzeStructure->processUnpairedOnReferenceStruct(10u, structure, diffStruct); //unpaired
    EXPECT_EQ(diffStruct, "00+--00---0");

    analyzeStructure->processUnpairedOnReferenceStruct(20u, structure, diffStruct); //unpaired
    EXPECT_EQ(diffStruct, "00+--00---00");
}

TEST_F(AnalyzeStructureSuiteTest, processPairedOnReferenceStruct)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure referenceStructure;
    biopp::SecStructure currentStructure;
    analyzeStructure->_folder->fold(sequence, _args.circular, referenceStructure, _args.initialRangeTemp);
    analyzeStructure->_folder->fold(sequence, _args.circular, currentStructure, _args.initialRangeTemp + 1);

    std::string diffStruct = "00";
    analyzeStructure->processPairedOnReferenceStruct(10u, currentStructure, referenceStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00+");

    analyzeStructure->processPairedOnReferenceStruct(16u, currentStructure, referenceStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00++");

    analyzeStructure->processPairedOnReferenceStruct(13u, currentStructure, referenceStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00++0");

    analyzeStructure->processPairedOnReferenceStruct(24u, currentStructure, referenceStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00++00");

    analyzeStructure->processPairedOnReferenceStruct(21u, currentStructure, referenceStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00++00+");
}

TEST_F(AnalyzeStructureSuiteTest, calculateDerivedStructureData)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure referenceStructure;
    biopp::SecStructure currentStructure;
    analyzeStructure->_folder->fold(sequence, _args.circular, referenceStructure, _args.initialRangeTemp);
    analyzeStructure->_folder->fold(sequence, _args.circular, currentStructure, _args.finalRangeTemp);

    std::string diffStruct;
    analyzeStructure->calculateDerivedStructureData(1u, 8u, referenceStructure, currentStructure, diffStruct);
    EXPECT_EQ(diffStruct, "00000000");
}

TEST_F(AnalyzeStructureSuiteTest, processWindowInvalidAmountOfStrucutures)
{
    const biopp::NucSequence sequence("CCAAUGCAUUCUGGACAGGCGGCCU");
    biopp::SecStructure structure;
    analyzeStructure->_folder->fold(sequence, _args.circular, structure, _args.initialRangeTemp);
    AnalyzeStructure::SequencesFoldedContainer foldedContainer;
    mili::insert_into(foldedContainer._structures, structure);
    EXPECT_THROW(analyzeStructure->processWindow(0u, foldedContainer), InvalidSize);
}

TEST_F(AnalyzeStructureSuiteTest, getDescriptionName)
{
    const std::string rnaDescription1(">gi|3721951|gb|U71132.1|IAU71132 Influenza A virus H3N2 A/Akiua/1/94 PB2 polymerase subuniu (PB2) gene, compleue cds");
    std::string sequenceName;
    analyzeStructure->getDescriptionName(rnaDescription1, sequenceName);
    EXPECT_EQ(sequenceName, "U71132.1");

    const std::string rnaDescription2(">gi|U71132.1|IAU71132 Influenza A virus");
    analyzeStructure->getDescriptionName(rnaDescription2, sequenceName);
    EXPECT_EQ(sequenceName, ">gi|U71132.1|IAU71132 Influenza A virus");
}